/*****************************************************************************
*                     Organizacion de Datos - 75.06                          *
*                         Catedra Lic. Servetto                              *
*            Facultad de Ingenieria - Universidad de Buenos Aires            *
******************************************************************************/ 

#include "../Comun/Archivo.h"
#include "AdministradorDeArchivos.h"
#include "ArchivoEnDisco.h"
#include "Contenedor.h"
#include "Serializador.h"
#include <fstream>
#include <string>
#include <set>
#include <iostream>

using namespace std;
using namespace Comun;

namespace CapaFisica
{

	typedef map<string, ArchivoEnDisco>::iterator IteradorDeArchivosEnDisco;

	const char* NombreDelArchivoDeRepositorio = "Datos/repositorio_archivos.txt";
	const int TamanioDelMapaDeBitsPorDefecto = 8192;

	AdministradorDeArchivos* AdministradorDeArchivos::instancia = 0;

	AdministradorDeArchivos& AdministradorDeArchivos::Instancia()
	{
		if (AdministradorDeArchivos::instancia == 0)
			AdministradorDeArchivos::instancia = new AdministradorDeArchivos();

		return *AdministradorDeArchivos::instancia;
	};

	AdministradorDeArchivos::AdministradorDeArchivos()
	{
		this->archivos = new map<string, ArchivoEnDisco>();

		if (!ExisteRepositorioDeArchivos())
			CrearRepositorioDeArchivos();

		CargarRepositorioDeArchivosEnMemoria();
	};

	AdministradorDeArchivos::~AdministradorDeArchivos()
	{
		delete( this->archivos );
	};

	bool AdministradorDeArchivos::ExisteClase(string clase)
	{
		return (this->archivos->find(clase) != this->archivos->end());
	};

	ArchivoEnDisco& AdministradorDeArchivos::RecuperarClase(string clase)
	{
		IteradorDeArchivosEnDisco encontrado = this->archivos->find(clase);
		
		if (encontrado == this->archivos->end())
		{
			cerr << "CapaFisica->AdministradorDeArchivos.cpp->AdministradorDeArchivos::RecuperarClase(string clase)" << endl << "La clase '" << clase << "' no se puede recuperar por que no existe en el repositorio." << endl;
			throw new exception();
		}

		return (*encontrado).second;
	};

	void AdministradorDeArchivos::CrearClaseParaRF(MetadatosArchivo& metadatosArchivo)
	{
		if (ExisteClase(metadatosArchivo.Nombre))
		{
			cerr <<"CapaFisica->AdministradorDeArchivos.cpp->AdministradorDeArchivos::CrearClaseParaRF(MetadatosArchivo& metadatosArchivo)" << endl << "La clase especificada no se puede crear por que ya existe." << endl;
			throw new exception();
		}

		ArchivoEnDisco* archivo = new ArchivoEnDisco(&metadatosArchivo, new MapaDeBits(metadatosArchivo.Nombre, TamanioDelMapaDeBitsPorDefecto));

		AgregarArchivoAlRepositorio(metadatosArchivo, false);

		this->archivos->insert(*(new pair<string, ArchivoEnDisco>(metadatosArchivo.Nombre, *archivo)));
	};

	void AdministradorDeArchivos::CrearClaseParaRLV(MetadatosArchivo& metadatosArchivo)
	{
		if (ExisteClase(metadatosArchivo.Nombre))
		{
			cerr << "CapaFisica->AdministradorDeArchivos.cpp->AdministradorDeArchivos::CrearClaseParaRLV(MetadatosArchivo& metadatosArchivo)" << endl << "La clase especificada no se puede crear por que ya existe." << endl;
			throw new exception();
		}

		ArchivoEnDisco* archivo = new ArchivoEnDisco(&metadatosArchivo, new MapaExtension(metadatosArchivo.Nombre, metadatosArchivo.TamanioBloque), metadatosArchivo.TamanioBloque);

		AgregarArchivoAlRepositorio(metadatosArchivo, true);

		this->archivos->insert(*(new pair<string, ArchivoEnDisco>(metadatosArchivo.Nombre, *archivo)));
	};

	int AdministradorDeArchivos::CrearGrupo(string clase, Comun::Grupo& grupo)
	{
		ArchivoEnDisco& archivo = RecuperarClase(clase);
		return archivo.CrearGrupo(grupo);
	};

	int AdministradorDeArchivos::GuardarGrupo(bool omitirSubflujo, string clase, Comun::Grupo& grupo)
	{
		ArchivoEnDisco& archivo = RecuperarClase(clase);
		return archivo.GuardarGrupo(grupo, omitirSubflujo);
	};

	Comun::Grupo& AdministradorDeArchivos::LeerGrupo(string clase, int grupoId)
	{
		ArchivoEnDisco& archivo = RecuperarClase(clase);
		return archivo.LeerGrupo(grupoId);
	};

	void AdministradorDeArchivos::BorrarGrupo(string clase, int grupoId)
	{
		ArchivoEnDisco& archivo = RecuperarClase(clase);
		archivo.BorrarGrupo(grupoId);
	};

	int AdministradorDeArchivos::PrimerGrupo(string clase)
	{
		ArchivoEnDisco& archivo = RecuperarClase(clase);
		return archivo.PrimerGrupo();
	};

	int AdministradorDeArchivos::SiguienteGrupo(string clase, int grupoIdActual)
	{
		ArchivoEnDisco& archivo = RecuperarClase(clase);
		return archivo.SiguienteGrupo(grupoIdActual);
	};

	bool AdministradorDeArchivos::ExisteRepositorioDeArchivos()
	{
		ifstream& repositorio = *(new ifstream(NombreDelArchivoDeRepositorio));

		return repositorio;
	};

	void AdministradorDeArchivos::CrearRepositorioDeArchivos()
	{
		ofstream& repositorio = *(new ofstream(NombreDelArchivoDeRepositorio));
		repositorio.close();
	};

	void AdministradorDeArchivos::CargarRepositorioDeArchivosEnMemoria()
	{
		ifstream& repositorio = *(new ifstream(NombreDelArchivoDeRepositorio));
		
		if (!repositorio)
		{
			cerr << "CapaFisica->AdministradorDeArchivos.cpp->AdministradorDeArchivos::CargarRepositorioDeArchivosEnMemoria()" << endl <<  "No se pudo abrir el repositorio de clases." << endl;
			throw new exception();
		}

		string lineaActual;
		int tamanioBloque;

		while(repositorio >> lineaActual)
		{
			string clase = lineaActual;

			MetadatosArchivo& metadatosTemporales = *(new MetadatosArchivo(clase));

			bool corroborarTamanios;
			repositorio >> corroborarTamanios;

			int tamanioBloque;
			repositorio >> tamanioBloque;
			metadatosTemporales.TamanioBloque = tamanioBloque;

			int cantidadAtributos;
			repositorio >> cantidadAtributos;
			metadatosTemporales.MetadatosGrupo = new MetadatosGrupo();
			for(int i = 0; i < cantidadAtributos; ++i)
			{
				string nombreAtributo;
				repositorio >> nombreAtributo;
				string tipoAtributo;
				repositorio >> tipoAtributo;
				MetadatosAtributo& metadatosAtributo = *(new MetadatosAtributo(nombreAtributo,ParserDeTipoRegistro::Parsear(tipoAtributo)));
				metadatosTemporales.MetadatosGrupo->MetadatosAtributos.push_back(metadatosAtributo);
			}

			int cantidadAtributosDeLosRegistros;
			repositorio >> cantidadAtributosDeLosRegistros;
			metadatosTemporales.MetadatosGrupo->MetadatosRegistro = new MetadatosRegistro();
			for(int j = 0; j < cantidadAtributosDeLosRegistros; ++j)
			{
				string nombreAtributo;
				repositorio >> nombreAtributo;
				string tipoAtributo;
				repositorio >> tipoAtributo;
				MetadatosAtributo& metadatosAtributo = *(new MetadatosAtributo(nombreAtributo,ParserDeTipoRegistro::Parsear(tipoAtributo)));
				metadatosTemporales.MetadatosGrupo->MetadatosRegistro->MetadatosAtributos.push_back(metadatosAtributo);
			}

			ArchivoEnDisco* archivoTemporal;
	
			if (!corroborarTamanios)
				archivoTemporal = new ArchivoEnDisco(&metadatosTemporales, new MapaDeBits(clase, TamanioDelMapaDeBitsPorDefecto), true);
			else
				archivoTemporal = new ArchivoEnDisco(&metadatosTemporales, new MapaExtension(clase, tamanioBloque), tamanioBloque);

			this->archivos->insert(*(new pair<string, ArchivoEnDisco>(clase, *archivoTemporal)));
		}

		//delete( repositorio );
	};

	void AdministradorDeArchivos::AgregarArchivoAlRepositorio(MetadatosArchivo& metadatosArchivo, bool corroborarTamanios)
	{
		ofstream& repositorio = *(new ofstream(NombreDelArchivoDeRepositorio, ios_base::app));

		if (!repositorio)
		{
			cerr << "CapaFisica->AdministradorDeArchivos.cpp->AdministradorDeArchivos::AgregarArchivoAlRepositorio(MetadatosArchivo& metadatosArchivo, bool corroborarTamanios)" << endl <<  "No se pudo abrir el repositorio de clases." << endl;
			throw new exception();
		}
		
		repositorio << metadatosArchivo.Nombre.c_str() << endl;
		repositorio << corroborarTamanios << endl;
		repositorio << metadatosArchivo.TamanioBloque << endl;

		repositorio << metadatosArchivo.MetadatosGrupo->MetadatosAtributos.size() << endl;
		for(IteradorDeMetadatosDeAtributos i = metadatosArchivo.MetadatosGrupo->MetadatosAtributos.begin(); i != metadatosArchivo.MetadatosGrupo->MetadatosAtributos.end(); ++i)
		{
			MetadatosAtributo metadatosAtributo = *i;
			repositorio << metadatosAtributo.Nombre.c_str() << endl;
			repositorio << ParserDeTipoRegistro::Parsear(metadatosAtributo.Tipo).c_str() << endl;
		}

		repositorio << metadatosArchivo.MetadatosGrupo->MetadatosRegistro->MetadatosAtributos.size() << endl;
		for(IteradorDeMetadatosDeAtributos i = metadatosArchivo.MetadatosGrupo->MetadatosRegistro->MetadatosAtributos.begin(); i != metadatosArchivo.MetadatosGrupo->MetadatosRegistro->MetadatosAtributos.end(); ++i)
		{
			MetadatosAtributo metadatosAtributo = *i;
			repositorio << metadatosAtributo.Nombre.c_str() << endl;
			repositorio << ParserDeTipoRegistro::Parsear(metadatosAtributo.Tipo).c_str() << endl;
		}

		repositorio.close();
	};

}
